﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace SpaceWolf
{
    public enum Action
    {
        PlanetData,
        MarketData,
        ShortGalaxyChart,
        CommanderData,
        LaunchMyShip,
        ShipSpeedUp,
        ShipSpeedDown,
        IncFlightRoll,
        DecFrlightRoll,
        IncFlightTurn,
        DecFrlightTurn,
        IncFlighClimb,
        DecFlighClimb,
        JumpToPlanet,
        NextTarget,
        PreviousTarget,
        DockingComputer,
        Afterburners,
        MilitaryData,
        Escape,

        TotalActionsCount
    }

    public class ActionMap
    {
        /// <summary>
        /// List of GamePad controls to be mapped to a given action.
        /// </summary>
        public List<GamePadButtons> gamePadButtons = new List<GamePadButtons>();

        /// <summary>
        /// List of Keyboard controls to be mapped to a given action.
        /// </summary>
        public List<Keys> keyboardKeys = new List<Keys>();
    }

    static class InputManager
    {
        /// <summary>
		/// Mouse state, set every frame in the Update method.
		/// </summary>
		private static MouseState mouseState,
			mouseStateLastFrame;
		/// <summary>
		/// Was a mouse detected? Returns true if the user moves the mouse.
		/// On the Xbox 360 there will be no mouse movement and theirfore we
		/// know that we don't have to display the mouse.
		/// </summary>
		private static bool mouseDetected = false;
        /// <summary>
		/// Mouse wheel delta this frame. We do not get the total
		/// scroll value, but we usually need the current delta!
		/// </summary>
		/// <returns>0</returns>
		private static int mouseWheelDelta = 0, mouseWheelValue = 0;
		/// <summary>
		/// Start dragging pos, will be set when we just pressed the left
		/// mouse button. Used for the MouseDraggingAmount property.
		/// </summary>
		private static Point startDraggingPos;

        private static KeyboardState CurrentKeyState;
        private static KeyboardState PrevKeyState;

        private static ActionMap[] actionMaps;

        public static bool IsActionTriggered(Action action)
        {
            return IsActionMapTriggered(actionMaps[(int)action]);
        }

        public static void ResetActionMap()
        {
            actionMaps = new ActionMap[(int)Action.TotalActionsCount];

            actionMaps[(int)Action.MarketData] = new ActionMap();
            actionMaps[(int)Action.MarketData].keyboardKeys.Add(Keys.K);

            actionMaps[(int)Action.PlanetData] = new ActionMap();
            actionMaps[(int)Action.PlanetData].keyboardKeys.Add(Keys.P);

            actionMaps[(int)Action.CommanderData] = new ActionMap();
            actionMaps[(int)Action.CommanderData].keyboardKeys.Add(Keys.L);

            actionMaps[(int)Action.LaunchMyShip] = new ActionMap();
            actionMaps[(int)Action.LaunchMyShip].keyboardKeys.Add(Keys.D1);

            actionMaps[(int)Action.ShipSpeedUp] = new ActionMap();
            actionMaps[(int)Action.ShipSpeedUp].keyboardKeys.Add(Keys.OemPlus);

            actionMaps[(int)Action.ShipSpeedDown] = new ActionMap();
            actionMaps[(int)Action.ShipSpeedDown].keyboardKeys.Add(Keys.OemMinus);

            actionMaps[(int)Action.IncFlightRoll] = new ActionMap();
            actionMaps[(int)Action.IncFlightRoll].keyboardKeys.Add(Keys.M);
            
            actionMaps[(int)Action.DecFrlightRoll] = new ActionMap();
            actionMaps[(int)Action.DecFrlightRoll].keyboardKeys.Add(Keys.N);

            actionMaps[(int)Action.IncFlightTurn] = new ActionMap();
            actionMaps[(int)Action.IncFlightTurn].keyboardKeys.Add(Keys.OemPeriod);

            actionMaps[(int)Action.DecFrlightTurn] = new ActionMap();
            actionMaps[(int)Action.DecFrlightTurn].keyboardKeys.Add(Keys.OemComma);

            actionMaps[(int)Action.DecFrlightRoll] = new ActionMap();
            actionMaps[(int)Action.DecFrlightRoll].keyboardKeys.Add(Keys.N);

            actionMaps[(int)Action.IncFlighClimb] = new ActionMap();
            actionMaps[(int)Action.IncFlighClimb].keyboardKeys.Add(Keys.V);

            actionMaps[(int)Action.DecFlighClimb] = new ActionMap();
            actionMaps[(int)Action.DecFlighClimb].keyboardKeys.Add(Keys.X);

            actionMaps[(int)Action.ShortGalaxyChart] = new ActionMap();
            actionMaps[(int)Action.ShortGalaxyChart].keyboardKeys.Add(Keys.O);

            actionMaps[(int)Action.JumpToPlanet] = new ActionMap();
            actionMaps[(int)Action.JumpToPlanet].keyboardKeys.Add(Keys.J);

            actionMaps[(int)Action.NextTarget] = new ActionMap();
            actionMaps[(int)Action.NextTarget].keyboardKeys.Add(Keys.A);

            actionMaps[(int)Action.PreviousTarget] = new ActionMap();
            actionMaps[(int)Action.PreviousTarget].keyboardKeys.Add(Keys.S);

            actionMaps[(int)Action.DockingComputer] = new ActionMap();
            actionMaps[(int)Action.DockingComputer].keyboardKeys.Add(Keys.C);
            
            actionMaps[(int)Action.Afterburners] = new ActionMap();
            actionMaps[(int)Action.Afterburners].keyboardKeys.Add(Keys.Tab);

            actionMaps[(int)Action.MilitaryData] = new ActionMap();
            actionMaps[(int)Action.MilitaryData].keyboardKeys.Add(Keys.D4);
            
            actionMaps[(int)Action.Escape] = new ActionMap();
            actionMaps[(int)Action.Escape].keyboardKeys.Add(Keys.Escape);
        }

        private static bool IsActionMapTriggered(ActionMap actionMap)
        {
            for (int i = 0; i < actionMap.keyboardKeys.Count; i++)
            {
                if (IsKeyTriggered(actionMap.keyboardKeys[i]))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Check if a key is pressed.
        /// </summary>
        public static bool IsKeyPressed(Keys key)
        {
            return CurrentKeyState.IsKeyDown(key);
        }

        /// <summary>
        /// Check if an action has been pressed.
        /// </summary>
        public static bool IsActionPressed(Action action)
        {
            return IsActionMapPressed(actionMaps[(int)action]);
        }

        /// <summary>
        /// Check if an action map has been pressed.
        /// </summary>
        private static bool IsActionMapPressed(ActionMap actionMap)
        {
            for (int i = 0; i < actionMap.keyboardKeys.Count; i++)
            {
                if (IsKeyPressed(actionMap.keyboardKeys[i]))
                {
                    return true;
                }
            }

            return false;
        }

        public static Action ActionTriggered()
        {
            return ActionMapTriggered();
        }
        /// <summary>
        /// Check if an action map has been triggered.
        /// </summary>
        private static Action ActionMapTriggered()
        {

            for (int i = 0; i < actionMaps.Length; i++)
            {
                if (IsActionMapTriggered(actionMaps[(int)i]))
                {
                    return (Action)i; //actionMap.keyboardKeys[i];
                }
            }

            return Action.TotalActionsCount;
        }

        public static Action ActionPressed()
        {
            return ActionMapPressed();
        }
        /// <summary>
        /// Check if an action map has been pressed.
        /// </summary>
        private static Action ActionMapPressed()
        {
            
            for (int i = 0; i < actionMaps.Length; i++)
            {
                if (IsActionMapPressed(actionMaps[(int)i]))
                {
                    return (Action)i; //actionMap.keyboardKeys[i];
                }
            }

            return Action.TotalActionsCount;
        }

        public static bool IsKeyTriggered(Keys key)
        {
            return (CurrentKeyState.IsKeyDown(key)) &&
                (!PrevKeyState.IsKeyDown(key));
        }

        public static void Update()
        {
            // Handle mouse input variables
			mouseStateLastFrame = mouseState;
			mouseState = Microsoft.Xna.Framework.Input.Mouse.GetState();

			// Update mouseXMovement and mouseYMovement
			lastMouseXMovement += mouseState.X - mouseStateLastFrame.X;
			lastMouseYMovement += mouseState.Y - mouseStateLastFrame.Y;
			mouseXMovement = lastMouseXMovement / 2.0f;
			mouseYMovement = lastMouseYMovement / 2.0f;
			lastMouseXMovement -= lastMouseXMovement / 2.0f;
			lastMouseYMovement -= lastMouseYMovement / 2.0f;

			if (MouseLeftButtonPressed == false)
				startDraggingPos = MousePos;
			mouseWheelDelta = mouseState.ScrollWheelValue - mouseWheelValue;
			mouseWheelValue = mouseState.ScrollWheelValue;

			// If we are in the game and don't show the mouse cursor anyway,
			// reset it to the center to allow moving it around.
            if (SpaceWolf.GameLogic.GameManager.InMenu == false)
                // App must be active
                
			{
				Microsoft.Xna.Framework.Input.Mouse.SetPosition(
					BaseGame.Width / 2, BaseGame.Height / 2);
				// Also use this for the current mouse pos for next frame,
				// else the mouseXMovement is messed up!
				mouseState = Microsoft.Xna.Framework.Input.Mouse.GetState();
			} // if (RacingGameManager.InMenu)

			// Check if mouse was moved this frame if it is not detected yet.
			// This allows us to ignore the mouse even when it is captured
			// on a windows machine if just the gamepad or keyboard is used.
            if (mouseDetected == false)// &&
                //always returns false: Microsoft.Xna.Framework.Input.Mouse.IsCaptured)
                mouseDetected = mouseState.X != mouseStateLastFrame.X ||
                    mouseState.Y != mouseStateLastFrame.Y ||
                    mouseState.LeftButton != mouseStateLastFrame.LeftButton;

            // update the keyboard state
            PrevKeyState = CurrentKeyState;
            CurrentKeyState = Keyboard.GetState();
        }

        #region Mouse Properties
        /// <summary>
        /// Was a mouse detected? Returns true if the user moves the mouse.
        /// On the Xbox 360 there will be no mouse movement and theirfore we
        /// know that we don't have to display the mouse.
        /// </summary>
        /// <returns>Bool</returns>
        public static bool MouseDetected
        {
            get
            {
                return mouseDetected;
            } // get
        } // MouseDetected

        /// <summary>
        /// Mouse position
        /// </summary>
        /// <returns>Point</returns>
        public static Point MousePos
        {
            get
            {
                return new Point(mouseState.X, mouseState.Y);
            } // get
        } // MousePos

        /// <summary>
        /// X and y movements of the mouse this frame
        /// </summary>
        private static float mouseXMovement, mouseYMovement,
            lastMouseXMovement, lastMouseYMovement;
        /// <summary>
        /// Mouse x movement
        /// </summary>
        /// <returns>Float</returns>
        public static float MouseXMovement
        {
            get
            {
                return mouseXMovement;
            } // get
        } // MouseXMovement

        /// <summary>
        /// Mouse y movement
        /// </summary>
        /// <returns>Float</returns>
        public static float MouseYMovement
        {
            get
            {
                return mouseYMovement;
            } // get
        } // MouseYMovement

        /// <summary>
        /// Mouse left button pressed
        /// </summary>
        /// <returns>Bool</returns>
        public static bool MouseLeftButtonPressed
        {
            get
            {
                return mouseState.LeftButton == ButtonState.Pressed;
            } // get
        } // MouseLeftButtonPressed

        /// <summary>
        /// Mouse right button pressed
        /// </summary>
        /// <returns>Bool</returns>
        public static bool MouseRightButtonPressed
        {
            get
            {
                return mouseState.RightButton == ButtonState.Pressed;
            } // get
        } // MouseRightButtonPressed

        /// <summary>
        /// Mouse middle button pressed
        /// </summary>
        /// <returns>Bool</returns>
        public static bool MouseMiddleButtonPressed
        {
            get
            {
                return mouseState.MiddleButton == ButtonState.Pressed;
            } // get
        } // MouseMiddleButtonPressed

        /// <summary>
        /// Mouse left button just pressed
        /// </summary>
        /// <returns>Bool</returns>
        public static bool MouseLeftButtonJustPressed
        {
            get
            {
                return mouseState.LeftButton == ButtonState.Pressed &&
                    mouseStateLastFrame.LeftButton == ButtonState.Released;
            } // get
        } // MouseLeftButtonJustPressed

        /// <summary>
        /// Mouse right button just pressed
        /// </summary>
        /// <returns>Bool</returns>
        public static bool MouseRightButtonJustPressed
        {
            get
            {
                return mouseState.RightButton == ButtonState.Pressed &&
                    mouseStateLastFrame.RightButton == ButtonState.Released;
            } // get
        } // MouseRightButtonJustPressed

        /// <summary>
        /// Mouse dragging amount
        /// </summary>
        /// <returns>Point</returns>
        public static Point MouseDraggingAmount
        {
            get
            {
                return new Point(
                    startDraggingPos.X - MousePos.X,
                    startDraggingPos.Y - MousePos.Y);
            } // get
        } // MouseDraggingAmount

        /// <summary>
        /// Reset mouse dragging amount
        /// </summary>
        public static void ResetMouseDraggingAmount()
        {
            startDraggingPos = MousePos;
        } // ResetMouseDraggingAmount()

        /// <summary>
        /// Mouse wheel delta
        /// </summary>
        /// <returns>Int</returns>
        public static int MouseWheelDelta
        {
            get
            {
                return mouseWheelDelta;
            } // get
        } // MouseWheelDelta

        /// <summary>
        /// Mouse in box
        /// </summary>
        /// <param name="rect">Rectangle</param>
        /// <returns>Bool</returns>
        public static bool MouseInBox(Rectangle rect)
        {
            bool ret = mouseState.X >= rect.X &&
                mouseState.Y >= rect.Y &&
                mouseState.X < rect.Right &&
                mouseState.Y < rect.Bottom;
            bool lastRet = mouseStateLastFrame.X >= rect.X &&
                mouseStateLastFrame.Y >= rect.Y &&
                mouseStateLastFrame.X < rect.Right &&
                mouseStateLastFrame.Y < rect.Bottom;

            // Highlight happend?
            if (ret &&
                lastRet == false)
            { }

            return ret;
        } // MouseInBox(rect)

        /// <summary>
        /// Mouse in box relative
        /// </summary>
        /// <param name="rect">Rectangle</param>
        /// <returns>Bool</returns>
        public static bool MouseInBoxRelative(Rectangle rect)
        {
            float widthFactor = BaseGame.Width / 1024.0f;
            float heightFactor = BaseGame.Height / 640.0f;
            return MouseInBox(new Rectangle(
                (int)Math.Round(rect.X * widthFactor),
                (int)Math.Round(rect.Y * heightFactor),
                (int)Math.Round(rect.Right * widthFactor),
                (int)Math.Round(rect.Bottom * heightFactor)));
        } // MouseInBoxRelative(rect)
        #endregion
    }
}
